/*
 * Copyright (C) 2004, Velocityme Partnership
 * ContactableSessionBean.java
 *
 * Created on 15 January 2004, 08:04
 */

package com.velocityme.session;

import com.velocityme.entity.ChangeDeltaItemTypeBean;
import com.velocityme.entity.PermissionBean;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;
import com.velocityme.interfaces.*;
import com.velocityme.utility.DirectoryDTO;
import com.velocityme.utility.InvalidKeyException;
import com.velocityme.valueobjects.*;
import java.rmi.RemoteException;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;

/**
 *
 * @author  Robert Crida Work
 * @ejb.bean
 *           type="Stateless"
 *           cmp-version="2.x"
 *           name="ContactableSession"
 *           jndi-name="ejb/ContactableSession"
 *           view-type="local"
 *           transaction-type="Container"
 * @ejb.transaction type="Required"
 *
 * @ejb.util generate="physical"
 */
public class ContactableSessionBean implements SessionBean {
    
    private SessionContext m_context;
    
    private PermissionLocal m_permissionNodeView;
    
    /**
     * Determine how the contact details have changed and set up the value object
     * appropriately so that it performs additions, removals and updates when
     * set.
     *
     * @param p_contactableValue Contains new values and is updated to reflect changes
     * @param p_currentContactDetailLocals The current details in the entity bean
     *
     * @ejb.interface-method view-type="local"
     **/
    public void calcContactDetailChanges(com.velocityme.valueobjects.ContactableValue p_contactableValue,
                                         java.util.Collection p_currentContactDetailLocals) {
        Map currentContactDetailMap = new HashMap();
        if (p_currentContactDetailLocals != null) {
            Iterator it = p_currentContactDetailLocals.iterator();
            while(it.hasNext()) {
                ContactDetailLocal contactDetailLocal = (ContactDetailLocal)it.next();
                currentContactDetailMap.put(contactDetailLocal.getContactDetailId(), contactDetailLocal.getContactDetailValue());
            }
        }
        p_contactableValue.cleanContactDetailValue();
        ContactDetailValue[] details = p_contactableValue.getContactDetailValues();
        for (int i = 0; i < details.length; i++) {
            if (details[i].getContactDetailId() != null) {
                Integer id = details[i].getContactDetailId();
                ContactDetailValue currentValue = (ContactDetailValue)currentContactDetailMap.get(id);
                if (details[i].getValue().equals("")) {
                    p_contactableValue.removeContactDetailValue(currentValue);
                }
                else {
                    if (!details[i].equals(currentValue)) {
                        p_contactableValue.updateContactDetailValue(details[i]);
                    }
                }
            }
            else
                if (!details[i].getValue().equals("")) {
                    p_contactableValue.addContactDetailValue(details[i]);
                }
        }
    }
    
    /**
     * Update the change delta with details of the changes to contact details.
     *
     * @param p_changeDeltaLocal The change delta object
     * @param p_contactableValue The details about the contact details
     *
     * @ejb.interface-method view-type="local"
     **/
    public void addContactDetailDeltas(com.velocityme.interfaces.ChangeDeltaLocal p_changeDeltaLocal,
                                       com.velocityme.valueobjects.ContactableValue p_contactableValue) {
        Iterator i = p_contactableValue.getAddedContactDetailValues().iterator();
        while(i.hasNext()) {
            ContactDetailValue value = (ContactDetailValue)i.next();
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.ADD_CONTACT_DETAIL, value.getContactDetailTypeValue().getName() + ": " + value.getValue());
        }
        i = p_contactableValue.getUpdatedContactDetailValues().iterator();
        while(i.hasNext()) {
            ContactDetailValue value = (ContactDetailValue)i.next();
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.UPDATE_CONTACT_DETAIL, value.getContactDetailTypeValue().getName() + ": " + value.getValue());
        }
        i = p_contactableValue.getRemovedContactDetailValues().iterator();
        while(i.hasNext()) {
            ContactDetailValue value = (ContactDetailValue)i.next();
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.REMOVE_CONTACT_DETAIL, value.getContactDetailTypeValue().getName() + ": " + value.getValue());
        }
    }
    
    /**
     * Get a tree model of all the contactable entities in the database
     *
     * @param p_key User's security key
     *
     * @ejb.interface-method view-type="local"
     **/
    public javax.swing.tree.TreeModel getContactableValueTree(com.velocityme.interfaces.KeySessionLocal p_keyLocal) throws InvalidKeyException {
        try {
            if (p_keyLocal.isValid()) {
                DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
                NodeLocal rootNodeLocal = NodeUtil.getLocalHome().findSystemNodeByName("Setup Organisations");
                addContactableBranch(p_keyLocal, root, rootNodeLocal, false);
                return new DefaultTreeModel(root);
            }
            else
                throw new InvalidKeyException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
    private void addContactableBranch(KeySessionLocal p_keyLocal,
                                      DefaultMutableTreeNode parent,
                                      NodeLocal nodeLocal,
                                      boolean hasPermission) throws NamingException, CreateException, InvalidKeyException {
        // first add each child if it is contactable
        Iterator childIt = nodeLocal.getChildrenNodesLocal().iterator();
        while(childIt.hasNext()) {
            NodeLocal childLocal = (NodeLocal)childIt.next();
            if (childLocal.getType().equals("Contactable")) {
                ContactableLocal contactableLocal = childLocal.getContactableLocal();
                // make sure that the contactable hasn't been deleted!
                if (contactableLocal.getNodeLocal().getDBObjectLocal().getIsDeleted().booleanValue() == false) {
                    // if the user has permission to view the contactable then add it
                    if (hasPermission || SearchSessionUtil.getLocalHome().create().getAssignedPermissionsLocal(p_keyLocal, contactableLocal.getNodeLocal()).contains(m_permissionNodeView)) {
                        DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new ContactableValueToString(contactableLocal.getContactableValue()));
                        parent.add(treeNode);
                        addContactableBranch(p_keyLocal, treeNode, childLocal, true);
                    }
                    // but check the branches even if not permission to see this
                    else {
                        addContactableBranch(p_keyLocal, parent, childLocal, false);
                    }
                }
            }
        }
    }
    
    /**
     * Get a set of persons nested in the given contactable. If the contactable
     * is a person then the set contains just that person. However, if it is an
     * organisation or department then the full branch is searched and all persons
     * attached to each department is included.
     *
     * @param p_key User's security key
     * @param p_contactableLocal the contactable a the root of the search 
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Set getNestedPersonSet(com.velocityme.interfaces.KeySessionLocal p_keyLocal,
                                            com.velocityme.interfaces.ContactableLocal p_contactableLocal)
        throws InvalidKeyException {
        if (!p_keyLocal.isValid())
            throw new InvalidKeyException();
        Set personSet = new HashSet();
        if (p_contactableLocal.getType().equals("Person"))
            personSet.add(p_contactableLocal.getPersonLocal());
        else {
            recursivePersonAdd(personSet, p_contactableLocal);
        }
        return personSet;
    }
    
    private void recursivePersonAdd(java.util.Set p_personSet, com.velocityme.interfaces.ContactableLocal p_contactableLocal) {
        if (p_contactableLocal.getType().equals("Person")) {
            p_personSet.add(p_contactableLocal.getPersonLocal());
        }
        Iterator i = p_contactableLocal.getNodeLocal().getChildrenNodesLocal().iterator();
        while(i.hasNext()) {
            NodeLocal nodeLocal = (NodeLocal)i.next();
            if (nodeLocal.getType().equals("Contactable"))
                recursivePersonAdd(p_personSet, nodeLocal.getContactableLocal());
        }
    }
    
    /**
     * Get a set of contactables nested in the given contactable.
     *
     * @param p_key User's security key
     * @param p_contactableLocal the contactable a the root of the search 
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Set getNestedContactableSet(com.velocityme.interfaces.KeySessionLocal p_keyLocal,
                                                 com.velocityme.interfaces.ContactableLocal p_contactableLocal)
        throws InvalidKeyException {
        if (!p_keyLocal.isValid())
            throw new InvalidKeyException();
        Set contactableSet = new HashSet();
        recursiveContactableAdd(contactableSet, p_contactableLocal);
        return contactableSet;
    }
    
    private void recursiveContactableAdd(java.util.Set p_contactableSet, com.velocityme.interfaces.ContactableLocal p_contactableLocal) {
        Iterator i = p_contactableLocal.getNodeLocal().getChildrenNodesLocal().iterator();
        while(i.hasNext()) {
            NodeLocal nodeLocal = (NodeLocal)i.next();
            if (nodeLocal.getType().equals("Contactable")) {
                ContactableLocal contactableLocal = nodeLocal.getContactableLocal();
                p_contactableSet.add(contactableLocal);
                recursiveContactableAdd(p_contactableSet, contactableLocal);
            }
        }
    }
    
    /**
     * Get the visible directory for the user of the specified key.
     *
     * @param p_key User's security key
     *
     * @ejb.interface-method view-type="local"
     **/
    public com.velocityme.utility.DirectoryDTO getDirectoryDTO(com.velocityme.interfaces.KeySessionLocal p_keyLocal)
        throws InvalidKeyException {
        if (!p_keyLocal.isValid())
            throw new InvalidKeyException();
        try {
            Map typeMap = new HashMap();
            // get the contact detail type values that appear in the directory
            List contactDetailTypeLocals = new ArrayList(ContactDetailTypeUtil.getLocalHome().findInDirectory());
            Collections.sort(contactDetailTypeLocals, new Comparator() {
                public int compare(Object o1, Object o2) {
                    return ((ContactDetailTypeLocal)o1).getSequenceNumber().compareTo(((ContactDetailTypeLocal)o2).getSequenceNumber());
                }
            });
            ContactDetailTypeValue[] contactDetailTypeValues = new ContactDetailTypeValue[contactDetailTypeLocals.size()];
            Iterator i = contactDetailTypeLocals.iterator();
            int index = 0;
            while(i.hasNext()) {
                ContactDetailTypeLocal contactDetailTypeLocal = (ContactDetailTypeLocal)i.next();
                contactDetailTypeValues[index] = contactDetailTypeLocal.getContactDetailTypeValue();
                typeMap.put(contactDetailTypeLocal.getContactDetailTypeId(), new Integer(index));
                index++;
            }
            DirectoryDTO directoryDTO = new DirectoryDTO(contactDetailTypeValues);
            TreeModel treeModel = getContactableValueTree(p_keyLocal);
            recursiveAddContactable(treeModel, treeModel.getRoot(), typeMap, "", directoryDTO);
            return directoryDTO;
        } catch(FinderException e) {
            throw new EJBException(e);
        } catch(NamingException e) {
            throw new EJBException(e);
        }
    }
    
    private void recursiveAddContactable(TreeModel p_contactableTreeModel, Object p_node, Map p_typeMap, String p_path, DirectoryDTO p_directoryDTO) {
        Object userObject = ((DefaultMutableTreeNode)p_node).getUserObject();
        if (userObject instanceof ContactableValue) {
            ContactableValue contactableValue = (ContactableValue)userObject;
            int nodeId = contactableValue.getNodeValue().getNodeId().intValue();
            String name = contactableValue.toString();
            int type;
            if (contactableValue.getType().equals("Organisation"))
                type = DirectoryDTO.Row.ORGANISATION;
            else if (contactableValue.getType().equals("Department"))
                type = DirectoryDTO.Row.DEPARTMENT;
            else
                type = DirectoryDTO.Row.PERSON;
            String[] strings = new String[p_typeMap.size()];
            ContactDetailValue[] contactDetailValues = contactableValue.getContactDetailValues();
            for (int i = 0; i < contactDetailValues.length; i++) {
                Integer id = contactDetailValues[i].getContactDetailTypeValue().getContactDetailTypeId();
                if (p_typeMap.containsKey(id)) {
                    Integer index = (Integer)p_typeMap.get(id);
                    strings[index.intValue()] = contactDetailValues[i].getValue();
                }
            }
            p_directoryDTO.addRow(nodeId, type, p_path, name, strings);
            p_path += "/" + name;
        }
        for (int i = 0; i < p_contactableTreeModel.getChildCount(p_node); i++) {
            Object nextNode = p_contactableTreeModel.getChild(p_node, i);
            recursiveAddContactable(p_contactableTreeModel, nextNode, p_typeMap, p_path, p_directoryDTO);
        }
    }
    
    /**
     * Create the Session Bean.
     * @throws CreateException 
     */
    public void ejbCreate() throws CreateException {
        try {
            m_permissionNodeView = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.NODE_VIEW)));
        }
        catch(Exception e) {
            throw new CreateException(e.getMessage());
        }
    }

    public void ejbActivate() throws java.rmi.RemoteException {
    }
    
    public void ejbPassivate() throws java.rmi.RemoteException {
    }
    
    public void ejbRemove() throws java.rmi.RemoteException {
    }
    
    public void setSessionContext(javax.ejb.SessionContext sessionContext) throws java.rmi.RemoteException {
        m_context = sessionContext;
    }
    
}
